<?php

/**
 * This file is part of the Framework03 PHP framework.
 * @author Walter Tamboer
 * @package Framework03
 */

/**
 * The base class for all configuration readers and writers.
 */
class FW_Config implements Countable, Iterator
{
    /**
     * @var array The data container which holds all the configuration data.
     */
    private $data = array();
    
    /**
     * @var int The amount of configuration settings.
     */
    private $count = 0;
    
    /**
     * @var int The pointer position.
     */
    private $pointer = 0;
    
    /**
     * Initializes a new instance of this class.
     * @param array $data The data container which holds all the configuration data.
     */
    public function __construct(array $data = array())
    {
        $this->data = array();
        
        foreach ($data as $key => $value)
        {
            if (is_array($value))
                $this->data[$key] = new self($value);
            else
                $this->data[$key] = $value;
        }
        
        $this->count = count($this->data);
    }

    /**
     * Gets a settings from this configuration container.
     * @param string $name The name of the setting to get.
     * @return mixed Returns the value that is bound to the $name setting.
     */
    public function get($name)
    {
        if (!isset($this->data[$name]))
            throw new FW_Config_Exception("Can't get '$name', setting not found.");

        return $this->data[$name];
    }

    /**
     * Magic function so that $obj->value will work.
     * @param string $name
     * @return mixed
     */
    public function __get($name)
    {
        return $this->get($name);
    }

    /**
     * Sets a configuration option to the given value.
     * @param string $name The name of the setting to set.
     * @param mixed $value The value that belongs to this setting.
     * @return FW_Config
     */
    public function set($name, $value)
    {
        $this->data[$name] = $value;
        return $this;
    }

    /**
     * Magic function so that $obj->value will work.
     * @param string $name
     * @param mixed $value
     */
    public function __set($name, $value)
    {
        $this->set($name, $value);
    }

    /**
     * Checks if the given setting name is stored in this configuration class.
     * @param string $name The name of the setting to check for.
     * @return bool Returns true when the given setting isset; false otherwise.
     */
    public function contains($name)
    {
        return isset($this->data[$name]);
    }

    /**
     * Magic function so that isset($obj->value) will work.
     * @param string $name
     * @return bool Returns true when the given setting isset; false otherwise.
     */
    public function __isset($name)
    {
        return $this->contains($name);
    }
    
    /**
     * Merges the given FW_Config into this FW_Config instance and overrides existing settings.
     * @param FW_Config $instance
     * @return FW_Config
     */
    public function merge(FW_Config $instance)
    {
        foreach ($instance as $key => $item)
        {
            if (array_key_exists($key, $this->data))
            {
                if ($item instanceof FW_Config && $this->$key instanceof FW_Config)
                    $this->$key = $this->$key->merge($item);
                else
                    $this->$key = $item;
            }
            else
                $this->$key = $item;
        }

        return $this;
    }
    
    protected function mergeArrays(array $arr1, array $arr2)
    {
        $data = $arr1;
        
        foreach ($arr2 as $key => $value)
        {
            if ($value instanceof FW_Config)
            {
                if (!isset($data[$key]))
                    $data[$key] = new FW_Config();
                    
                $data[$key]->merge($value);
            }
            else
                $data[$key] = $value;
        }

        return $data;
    }

    //
    // ITERATOR SUPPORT
    //

    public function count()
    {
        return $this->count;
    }

    public function current()
    {
        return current($this->data);
    }

    public function key()
    {
        return key($this->data);
    }

    public function next()
    {
        next($this->data);
        $this->pointer++;
    }

    public function rewind()
    {
        reset($this->data);
        $this->pointer = 0;
    }

    public function valid()
    {
        return $this->pointer < $this->count;
    }
}
